import json
import random
from datetime import datetime, timedelta
from typing import List, Dict
from collections import deque
from people_flow_generator import Person


class SecurityGate:
    """安检口类，模拟安检口的排队和通过过程"""
    
    def __init__(self, gate_type: str, config_path: str):
        with open(config_path, 'r', encoding='utf-8') as f:
            self.config = json.load(f)
        
        self.gate_type = gate_type
        self.seconds_per_person = self.config["security_gates"][gate_type]["seconds_per_person"]
        self.num_checkpoints = self.config["security_gates"][gate_type]["num_checkpoints"]
        self.failure_rate = self.config["security_gates"][gate_type]["failure_rate"]
        self.retry_time_minutes = self.config["security_gates"][gate_type]["retry_time_minutes"]
        self.distance_to_E1 = self.config["security_gates"][gate_type]["distance_to_E1"]
        self.walking_speed = self.config["walking_speed"]
        
        # 安检口前的队列（蓄水池）
        self.queue = deque()
        
        # 记录安检口处理历史，用于计算等待时间
        self.processing_history = []
        
        # 记录需要重新安检的人员
        self.retry_queue = deque()
        
        # 安检点状态：每个安检点记录当前处理的人员和完成时间
        self.checkpoints = [None] * self.num_checkpoints  # None表示空闲，Person对象表示正在处理
        self.checkpoint_finish_times = [None] * self.num_checkpoints  # 每个安检点的完成时间
    
    def add_to_queue(self, person: Person, current_time: datetime):
        """将人员添加到安检口队列"""
        person.security_queue_time = current_time
        self.queue.append(person)
    
    def process_queue(self, current_time: datetime) -> List[Person]:
        """处理队列中的人员，支持多个安检点并行处理"""
        processed_people = []
        
        # 1. 检查哪些安检点已经完成处理
        for i in range(self.num_checkpoints):
            if self.checkpoints[i] is not None and self.checkpoint_finish_times[i] <= current_time:
                person = self.checkpoints[i]
                person.security_pass_time = current_time
                
                # 检查是否是重新安检的人员
                is_retry = getattr(person, 'security_failed', False) and hasattr(person, 'security_retry_count')
                
                # 如果是重新安检的人员，直接通过
                if is_retry:
                    person.security_failed = False
                    processed_people.append(person)
                else:
                    # 首次安检，模拟安检不通过的情况
                    if random.random() < self.failure_rate:
                        # 安检不通过，需要重新安检
                        person.security_failed = True
                        person.security_retry_time = current_time + timedelta(minutes=self.retry_time_minutes)
                        person.security_retry_count = 1  # 记录重试次数
                        self.retry_queue.append(person)
                    else:
                        person.security_failed = False
                        processed_people.append(person)
                
                # 记录处理历史
                self.processing_history.append({
                    'person_id': person.id,
                    'queue_time': person.security_queue_time,
                    'pass_time': person.security_pass_time,
                    'wait_time': (person.security_pass_time - person.security_queue_time).total_seconds() / 60,  # 分钟
                    'failed': getattr(person, 'security_failed', False),
                    'is_retry': is_retry
                })
                
                # 释放安检点
                self.checkpoints[i] = None
                self.checkpoint_finish_times[i] = None
        
        # 2. 将队列中的人员分配到空闲的安检点
        for i in range(self.num_checkpoints):
            # 如果安检点空闲且队列中还有人员
            if self.checkpoints[i] is None and self.queue:
                person = self.queue.popleft()
                self.checkpoints[i] = person
                # 计算完成时间 - 每个安检点独立处理，不需要除以安检点数量
                self.checkpoint_finish_times[i] = current_time + timedelta(seconds=self.seconds_per_person)
        
        return processed_people
    
    def process_retry_queue(self, current_time: datetime):
        """处理需要重新安检的人员"""
        retry_processed = []
        
        # 检查是否有人员到了重新安检的时间
        while self.retry_queue and self.retry_queue[0].security_retry_time <= current_time:
            person = self.retry_queue.popleft()
            # 重新安检，直接添加到队列前面
            self.queue.appendleft(person)
        
        return retry_processed
    
    def calculate_walking_time_to_E1(self) -> float:
        """计算从安检口到E1口的步行时间（分钟）"""
        return (self.distance_to_E1 / self.walking_speed) / 60  # 转换为分钟


class SecuritySimulation:
    """安检模拟器，模拟整个安检过程"""
    
    def __init__(self, config_path: str):
        with open(config_path, 'r', encoding='utf-8') as f:
            self.config = json.load(f)
        
        self.subway_exits = self.config["subway_exits"]
        
        # 创建大小安检口
        self.small_gate = SecurityGate("small", config_path)
        self.large_gate = SecurityGate("large", config_path)
        
        # 记录所有人员
        self.all_people = []
        
        # 记录模拟结果
        self.results = {
            'morning': {
                'total_people': 0,
                'avg_total_time': 0,
                'max_total_time': 0,
                'min_total_time': 0,
                'avg_wait_time': 0,
                'max_wait_time': 0,
                'failed_people': 0,
                'all_people_arrival_time': 0  # 新增：所有人员完全到达E1口所需的总时长
            },
            'afternoon': {
                'total_people': 0,
                'avg_total_time': 0,
                'max_total_time': 0,
                'min_total_time': 0,
                'avg_wait_time': 0,
                'max_wait_time': 0,
                'failed_people': 0,
                'all_people_arrival_time': 0  # 新增：所有人员完全到达E1口所需的总时长
            }
        }
    
    def simulate(self, people: List[Person], period_type: str):
        """模拟安检过程"""
        self.all_people = people
        
        if not people:
            return
        
        # 获取模拟的时间范围
        start_time = people[0].arrival_time
        # 计算最后一个人从地铁口到安检口的时间，并添加额外时间
        last_person = people[-1]
        last_exit_distance = self.subway_exits[last_person.exit_name]["distance_to_security"]
        walking_time_to_security = (last_exit_distance / self.config["walking_speed"]) / 60  # 分钟
        end_time = last_person.arrival_time + timedelta(hours=2, minutes=walking_time_to_security)  # 额外增加2小时+步行时间确保所有人都能通过安检
        
        # 按时间顺序模拟
        current_time = start_time
        time_step = timedelta(seconds=1)  # 每秒处理一次，提高模拟精度
        
        # 人员索引
        person_index = 0
        
        # 模拟从地铁口到安检口的储蓄池逻辑
        # 人员到达地铁口后，需要步行到安检口
        people_waiting_for_security = []  # 等待到达安检口的人员
        
        # 重置安检口状态
        self.small_gate.queue = deque()
        self.small_gate.retry_queue = deque()
        self.large_gate.queue = deque()
        self.large_gate.retry_queue = deque()
        
        while current_time <= end_time:
            # 处理重新安检队列
            self.small_gate.process_retry_queue(current_time)
            self.large_gate.process_retry_queue(current_time)
            
            # 将到达地铁口的人员添加到等待队列
            while person_index < len(people) and people[person_index].arrival_time <= current_time:
                person = people[person_index]
                # 计算从地铁口到安检口的步行时间
                distance_to_security = self.subway_exits[person.exit_name]["distance_to_security"]
                walking_time = (distance_to_security / self.config["walking_speed"]) / 60  # 分钟
                person.security_arrival_time = person.arrival_time + timedelta(minutes=walking_time)
                people_waiting_for_security.append(person)
                person_index += 1
            
            # 将到达安检口的人员添加到相应的安检口队列
            people_to_remove = []
            for person in people_waiting_for_security:
                if person.security_arrival_time <= current_time:
                    gate_type = self.subway_exits[person.exit_name]["security_gate"]
                    
                    if gate_type == "small":
                        self.small_gate.add_to_queue(person, current_time)
                    else:
                        self.large_gate.add_to_queue(person, current_time)
                    
                    people_to_remove.append(person)
            
            # 从等待队列中移除已添加到安检口队列的人员
            for person in people_to_remove:
                people_waiting_for_security.remove(person)
            
            # 处理安检口队列
            small_processed = self.small_gate.process_queue(current_time)
            large_processed = self.large_gate.process_queue(current_time)
            
            # 计算通过安检后到达E1口的时间
            for person in small_processed + large_processed:
                gate_type = self.subway_exits[person.exit_name]["security_gate"]
                walking_time = self.small_gate.calculate_walking_time_to_E1() if gate_type == "small" else self.large_gate.calculate_walking_time_to_E1()
                person.e1_arrival_time = person.security_pass_time + timedelta(minutes=walking_time)
            
            # 移动到下一分钟
            current_time += time_step
        
        # 计算统计结果
        self.calculate_statistics(period_type)
    
    def calculate_statistics(self, period_type: str):
        """计算统计结果"""
        if not self.all_people:
            return
        
        total_times = []
        wait_times = []
        failed_count = 0
        e1_arrival_times = []  # 新增：记录所有人员到达E1口的时间
        
        for person in self.all_people:
            # 计算总时间（从到达地铁口到到达E1口）
            if person.e1_arrival_time:
                total_time = (person.e1_arrival_time - person.arrival_time).total_seconds() / 60  # 分钟
                total_times.append(total_time)
                # 记录到达E1口的时间
                e1_arrival_times.append(person.e1_arrival_time)
            
            # 计算等待时间（从开始排队到通过安检）
            if person.security_queue_time and person.security_pass_time:
                wait_time = (person.security_pass_time - person.security_queue_time).total_seconds() / 60  # 分钟
                wait_times.append(wait_time)
            
            # 统计安检不通过的人数（检查是否有重试记录）
            if hasattr(person, 'security_retry_count') and person.security_retry_count > 0:
                failed_count += 1
        
        if total_times:
            self.results[period_type]['total_people'] = len(self.all_people)
            self.results[period_type]['avg_total_time'] = sum(total_times) / len(total_times)
            self.results[period_type]['max_total_time'] = max(total_times)
            self.results[period_type]['min_total_time'] = min(total_times)
        
        if wait_times:
            self.results[period_type]['avg_wait_time'] = sum(wait_times) / len(wait_times)
            self.results[period_type]['max_wait_time'] = max(wait_times)
        
        # 新增：计算所有人员完全到达E1口所需的总时长
        if e1_arrival_times:
            # 找出第一个到达地铁口的时间和最后一个到达E1口的时间
            first_arrival_time = min(person.arrival_time for person in self.all_people)
            last_e1_arrival_time = max(e1_arrival_times)
            # 计算从第一个人员到达地铁口到最后一个人员到达E1口的总时长
            all_people_arrival_time = (last_e1_arrival_time - first_arrival_time).total_seconds() / 60  # 分钟
            self.results[period_type]['all_people_arrival_time'] = all_people_arrival_time
        
        self.results[period_type]['failed_people'] = failed_count
    
    def get_results(self, period_type: str) -> Dict:
        """获取指定时间段的模拟结果"""
        return self.results.get(period_type, {})